﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.Samples.ServiceHosting.StorageClient;
using Ciripescu_WebRole.Entities;
using System.Data.Services.Client;

namespace Ciripescu_WebRole.BL
{
    public class UrmarireManager
    {
        #region Public Methods

        public static int m_nbOfUsersPerPage = Properties.Settings.Default.NbOfUsersPerPage;
        public UrmarireManager()
        {
            m_svcContext             = new MyServiceContext();
            m_svcContext.RetryPolicy = RetryPolicies.RetryN(3, TimeSpan.FromSeconds(1));
        }

        public IEnumerable<Urmarire> DoQuery(IQueryable<Urmarire> q)
        {
            TableStorageDataServiceQuery<Urmarire> query = new TableStorageDataServiceQuery<Urmarire>(q as DataServiceQuery<Urmarire>);
            IEnumerable<Urmarire> queryResults           = query.ExecuteAllWithRetries();
            
            return queryResults;
        }

        public Urmarire GetItem(string user, string followsUser)
        {
            try
            {
                return (DoQuery(from c in m_svcContext.Urmarire where c.RowKey == followsUser && c.PartitionKey == user select c)).Single();
            }
            catch (Exception e)
            {
                return null;
            }
        }

        /// <summary>
        /// Get a list of followed users (Urmarire objects - pairs of associations) by a given username
        /// Toti cei pe cate ii urmateste un anumit user
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public IEnumerable<Urmarire> GetFollowedUsers(string userName)
        {
            var results = from f in m_svcContext.Urmarire where f.PartitionKey == userName select f;
            return DoQuery(results);
        }

        /// <summary>
        /// Gets a list of users followed by the user given as input.
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public List<User> GetFollowedByUser(string userName)
        {
            List<User> result  = new List<User>();
            UserManager uManag = new UserManager();
            foreach (Urmarire um in GetFollowedUsers(userName).ToList<Urmarire>())
            {
                result.Add(uManag.GetItem(um.getFollowdUser()));
            }

            return result;
        }

        /// <summary>
        /// Gets a list of users followed by the user given as input for a given page.If page is negative than
        /// it returns all the users.
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public List<User> GetFollowedByUser(string userName,int pageNumber)
        {
            List<User> result   = new List<User>();
            UserManager uManag  = new UserManager();
            List<User> toReturn = new List<User>();

            foreach (Urmarire um in GetFollowedUsers(userName).ToList<Urmarire>())
            {
                result.Add(uManag.GetItem(um.getFollowdUser()));
            }

            if (pageNumber <= 0)
                toReturn = result;

            // verifica daca exista pagina
            if (result.Count > (pageNumber - 1) * m_nbOfUsersPerPage)
            {
                if (m_nbOfUsersPerPage * pageNumber > result.Count) // este ultima pagina
                    toReturn = result.GetRange(m_nbOfUsersPerPage * (pageNumber - 1), 
                                               result.Count - m_nbOfUsersPerPage * (pageNumber - 1));
                else // pagina oarecare
                    toReturn = result.GetRange(m_nbOfUsersPerPage * (pageNumber - 1), m_nbOfUsersPerPage);
            }
            //
            else 
                toReturn = new List<User>();// daca s-a cerut pagina care nu exista intoarce lista vida

            return toReturn;
        }

        /// <summary>
        /// Returns the top n users on which the current user is following.
        /// </summary>
        /// <param name="n"></param>
        /// <returns></returns>
        public List<User> GetFollowingTopN(string userName,int n)
        {
            List<User> allFollowed = GetFollowedByUser(userName);
            if (allFollowed.Count > n+5) //n+5 in order to have with 5 more users(than n) at least
            {
                //we try to randomize getting the objects
                Random r = new Random();
                int rand = r.Next(0,allFollowed.Count - n);
                List<User> myList = allFollowed.GetRange(rand, n);
                return myList;
                //return (List<User>)allFollowed.Take(n);
            }
            else
                return allFollowed;

        }

        /// <summary>
        /// Get a list of not followed users (Urmarire objects - pairs of associations) by a given username
        /// Toti cei pe cate nu ii urmateste un anumit user
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public IEnumerable<User> GetNotFollowedUsers(string userName)
        {
            //var q = from u in m_svcContext.Users where !(from urm in m_svcContext.Urmarire 
            //                                                   where urm.PartitionKey == userName
            //                                                   select urm.RowKey).Contains(u.PartitionKey) select u;
            UserManager um           = new UserManager();
            List<User> allUsers      = um.GetAllItems().ToList<User>();
     
            List<Urmarire> urmarire  = GetFollowedUsers(userName).ToList<Urmarire>();

            foreach(Urmarire u in urmarire)
            {
                allUsers.Remove(um.GetItem(u.getFollowdUser()));
            }
            allUsers.Remove(um.GetItem(userName));
            return allUsers;
        }

        public IEnumerable<Urmarire> GetAllItems()
        {
            var results = from c in m_svcContext.Urmarire select c;
            return DoQuery(results);
        }

        /// <summary>
        /// Gets the pairs for the users that are followers for the given user.
        /// Urmaririle pentru care userul dat este urmarit. Urmaritorii userului dat.
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public IEnumerable<Urmarire> GetFollowersUsers(string userName)
        {
            var results = from f in m_svcContext.Urmarire where f.RowKey == userName select f;
            return DoQuery(results);
        }

        /// <summary>
        /// Gets the list of followers for the given user.
        /// Lista urmaritorilor pentru un user.
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public List<User> GetFollowersByUser(string userName)
        {
            List<User> result  = new List<User>();
            UserManager uManag = new UserManager();
            foreach (Urmarire um in GetFollowersUsers(userName).ToList<Urmarire>())
            {
                result.Add(uManag.GetItem(um.PartitionKey));
            }

            return result;
        }

        /// <summary>
        /// Gets the list of followers for the given user and the page.
        /// Lista urmaritorilor pentru un user.
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public List<User> GetFollowersByUser(string userName,int pageNumber)
        {
            List<User> result   = new List<User>();
            UserManager uManag  = new UserManager();
            List<User> toReturn = new List<User>();

            foreach (Urmarire um in GetFollowersUsers(userName).ToList<Urmarire>())
            {
                result.Add(uManag.GetItem(um.PartitionKey));
            }

            if (pageNumber <= 0)
                toReturn = result;

            // verifica daca exista pagina
            if (result.Count > (pageNumber - 1) * m_nbOfUsersPerPage)
            {
                if (m_nbOfUsersPerPage * pageNumber > result.Count) // este ultima pagina
                    toReturn = result.GetRange(m_nbOfUsersPerPage * (pageNumber - 1),
                                               result.Count - m_nbOfUsersPerPage * (pageNumber - 1));
                else // pagina oarecare
                    toReturn = result.GetRange(m_nbOfUsersPerPage * (pageNumber - 1), m_nbOfUsersPerPage);
            }
            //
            else
                toReturn = new List<User>();// daca s-a cerut pagina care nu exista intoarce lista vida

            return toReturn;
        }


        /// <summary>
        /// Gets the number of users followed by the given user.
        /// Returneaza numarul de urmariti.
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public int GetNrUrmariti(string userName)
        {
            return GetFollowedByUser(userName).Count;
        }

        /// <summary>
        /// Gets the number of users that are followers for the given user.
        /// Returneaza numarul de urmaritori.
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public int GetNrUrmaritori(string userName)
        {
            return GetFollowersByUser(userName).Count;
        }

        public void Delete(Urmarire itemToDelete)
        {
            //decrementare nr. urmariti pt partition key
            //decrementare nr urmaritori pt rowkey
            string pkUserName = itemToDelete.PartitionKey;
            string rkUserName = itemToDelete.RowKey;

            m_svcContext.DeleteObject(itemToDelete);
            m_svcContext.SaveChanges();

            UserManager um = new UserManager();
            User pk = um.GetItem(pkUserName);
            pk.NrUrmariti = GetNrUrmariti(pkUserName);
            um.Save(pk);
            User rk = um.GetItem(rkUserName);
            rk.NrUrmaritori = GetNrUrmaritori(rkUserName);
            um.Save(rk);
        }

        public void DeleteWithoutQ(Urmarire itemToDelete)
        {
            m_svcContext.AttachTo(m_svcContext.FollowersTableName, itemToDelete, "*");
            Delete(itemToDelete);
        }

        /// <summary>
        /// Updates an existing item in the tables.
        /// </summary>
        /// <param name="item"></param>
        public void Save(Urmarire item)
        {
            
            m_svcContext.UpdateObject(item);
            m_svcContext.SaveChanges();

        }

        /// <summary>
        /// Inserts a new item in the tables.
        /// </summary>
        /// <param name="newItem"></param>
        public void Insert(Urmarire newItem)
        {
            //incrementare nr. urmariti pt partitionkey
            //incrementare nr urmaritori pt rowkey
            string pkUserName = newItem.PartitionKey;
            string rkUserName = newItem.RowKey;

            m_svcContext.AddObject(m_svcContext.FollowersTableName, newItem);
            m_svcContext.SaveChanges();


            UserManager um = new UserManager();
            User pk = um.GetItem(pkUserName);
            pk.NrUrmariti = GetNrUrmariti(pkUserName);
            um.Save(pk);
            User rk = um.GetItem(rkUserName);
            rk.NrUrmaritori = GetNrUrmaritori(rkUserName);
            um.Save(rk);

        }


        #endregion 

        #region Private Methods
        private MyServiceContext m_svcContext = null;
        #endregion
    }
}
